/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.ext.echarts;

import cn.easyplatform.lang.Strings;
import cn.easyplatform.web.annotation.Builder;
import cn.easyplatform.web.ext.Reloadable;
import cn.easyplatform.web.ext.Widget;
import cn.easyplatform.web.ext.ZkExt;
import cn.easyplatform.web.ext.echarts.lib.Option;
import cn.easyplatform.web.ext.echarts.lib.series.Map;
import cn.easyplatform.web.ext.echarts.lib.type.Tool;
import cn.easyplatform.web.ext.echarts.util.EChartsUtils;
import cn.easyplatform.web.ext.echarts.util.GsonUtil;
import cn.easyplatform.web.ext.echarts.util.JSFunction;
import cn.easyplatform.web.ext.zul.Explorer;
import com.alibaba.fastjson.JSON;
import org.zkoss.json.JavaScriptValue;
import org.zkoss.lang.Objects;
import org.zkoss.util.resource.Locators;
import org.zkoss.zk.au.AuRequest;
import org.zkoss.zk.ui.HtmlBasedComponent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Clients;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
@Builder(EChartsBuilder.class)
public class ECharts extends HtmlBasedComponent implements ZkExt, Reloadable {

    public static final String ON_DATA_URL = "onDataURL";
    public static final String ON_GRAPH = "onGraph";
    static {
        addClientEvent(ECharts.class, ON_DATA_URL, CE_IMPORTANT | CE_REPEAT_IGNORE);
        addClientEvent(ECharts.class, ON_GRAPH, CE_IMPORTANT | CE_REPEAT_IGNORE);
    }

    /**
     * 数据连接的资源id
     */
    private String _dbId;

    /**
     * 查询语句
     */
    private Object _query;

    /**
     * 直接数据
     */
    private Object _data;

    /**
     * 图片URL
     */
    private String _dataURL;

    /**
     * 在显示时是否要马上执行报表
     */
    private boolean _immediate = true;

    /**
     * 过滤表达式
     */
    private String _filter;

    /**
     * 是否必需重新加载
     */
    private boolean _force;

    /**
     * 风格
     */
    private String _theme;

    /**
     * 图表类型
     */
    private String type;

    /**
     * 使用的模板
     */
    private String template;

    /**
     * 工具栏
     */
    private String toolbox;

    /**
     * 配置项
     */
    private Option option = new Option();

    /**
     * 配置项json->base64
     */
    private String content;

    /**
     * sql查询字段序列
     */
    private Set<String> sequence = new LinkedHashSet();

    /**
     * 渲染方法类型
     */
    private String createType;

    @Override
    public boolean isForce() {
        return _force;
    }

    public void setForce(boolean force) {
        this._force = force;
    }

    public ECharts() {
    }

    public String getTheme() {
        return _theme;
    }

    public void setTheme(String theme) {
        if (!Objects.equals(_theme, theme)) {
            this._theme = theme;
            smartUpdate("theme", _theme);
        }
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getTemplate() {
        return template;
    }

    public void setTemplate(String template) {
        this.template = template;
    }

    protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer)
            throws java.io.IOException {
        super.renderProperties(renderer);
        if (option.getSeries() instanceof Map) {
            Map map = (Map) option.getSeries();
            if (!Strings.isBlank(map.getMap()))
                renderer.render("map", map.getMap());
        }
        if (option.getGeo() != null) {
            String map = (String) ((java.util.Map) option.getGeo()).get("map");
            if (Strings.isBlank(map))
                map = "china";
            renderer.render("map", map);
        }
        if (!Strings.isBlank(_theme))
            renderer.render("theme", _theme);
        String gson = GsonUtil.prettyFormat(option);
        renderer.render("options", gson);
    }

    public String getToolbox() {
        return this.toolbox;
    }

    public void setToolbox(String toolbox) {
        this.toolbox = toolbox;
        if (!Strings.isBlank(toolbox)) {
            String[] features = toolbox.split(";");
            Tool[] tools = new Tool[features.length];
            for (int i = 0; i < tools.length; i++)
                tools[i] = Tool.valueOf(features[i]);
            option.toolbox().feature(tools);
        }
    }

    public void series(Object serial) {
        this.option.series(serial);
    }

    /**
     * 以下部分关于业务的属性
     */
    public String getFilter() {
        return _filter;
    }

    public void setFilter(String filter) {
        this._filter = filter;
    }

    public String getDbId() {
        return _dbId;
    }

    public void setDbId(String dbId) {
        this._dbId = dbId;
    }

    public Object getQuery() {
        return _query;
    }

    public void setQuery(Object query) {
        this._query = query;
    }

    public Object getData() {
        return _data;
    }

    public void setData(Object data) {
        if (data != null) {
            this._data = data;
            this._query = null;
            reload();
        }
    }

    public void setDataURL(String dataURL) {
        this._dataURL = dataURL;
    }

    public String getDataURL() {
        return this._dataURL;
    }

    public boolean isImmediate() {
        return _immediate;
    }

    public void setImmediate(boolean immediate) {
        this._immediate = immediate;
    }

    public String getContent() {
        EChartsUtils.clear(this.option);
        this.content = new String(Base64.getEncoder().encode(GsonUtil.format(this.option).getBytes(StandardCharsets.UTF_8)));
        return content;
    }

    public void setContent(String content) {
        this.content = content;
        if (Strings.isBlank(content)) {
            this.option = new Option();
        } else {
            this.option = GsonUtil.fromJson(new String(Base64.getDecoder().decode(content), StandardCharsets.UTF_8));
        }
    }

    public String toJson() {
        return GsonUtil.format(this.option);
    }

    public Option getOption() {
        return option;
    }

    public void setOption(Option options) {
        this.option = options;
        smartUpdate("options", GsonUtil.prettyFormat(options));
    }

    public Set<String> getSequence() {
        return sequence;
    }

    public void setSequence(Set<String> sequence) {
        this.sequence = sequence;
    }

    public String getCreateType() {
        return createType;
    }

    public void setCreateType(String createType) {
        this.createType = createType;
    }

    public void showLoading(String text) {
        JSFunction js = new JSFunction();
        js.callFunction("showLoading", new Object[]{text});
        smartUpdate("eval", new JavaScriptValue(js.toWrapFunction()), true);
    }

    public void hideLoading() {
        JSFunction js = new JSFunction();
        js.callFunction("hideLoading");
        smartUpdate("eval", new JavaScriptValue(js.toWrapFunction()), true);
    }

    public void print() {
        JSFunction js = new JSFunction();
        js.callFunction("print");
        smartUpdate("eval", new JavaScriptValue(js.toWrapFunction()), true);
    }

    @Override
    public void reload() {
        Widget ext = (Widget) getAttribute("$proxy");
        ext.reload(this);
        this.setOption(this.option);
    }

    @Override
    public Object clone() {
        ECharts clone = (ECharts) super.clone();
        return clone;
    }

    public void service(AuRequest request, boolean everError) {
        final String cmd = request.getCommand();
        final Object data = request.getData().get("data");
        switch (cmd) {
            case Events.ON_CLICK:
            case Events.ON_MOUSE_OVER:
            case ON_DATA_URL:
            case ON_GRAPH: {
                Event evt = new Event(cmd, this, JSON.toJSONString(data));
                Events.postEvent(evt);
                break;
            }
            default:
                super.service(request, everError);
                break;
        }
    }

    /**
     * init之后延时1秒生成base64
     */
    public void initDataURL() {
        getOption().setAnimation(false);
        Clients.evalJavaScript("zk.$('" + this.getUuid() + "').initDataURL()");
    }

    /**
     * 立即生成base64，注意若未init会报空指针
     */
    public void fireDataURL() {
        getOption().setAnimation(false);
        Clients.evalJavaScript("zk.$('" + this.getUuid() + "').fireDataURL()");
    }

    /**
     * init之后延时1秒取得关系图坐标
     */
    public void initGraphItemLayouts() {
        Clients.evalJavaScript("zk.$('" + this.getUuid() + "').initGraphItemLayouts()");
    }

    /**
     * 立即取到坐标，注意未init会报空指针
     */
    public void getGraphItemLayouts() {
        Clients.evalJavaScript("zk.$('" + this.getUuid() + "').graphItemLayouts()");
    }

    public void clearInit() {
        Clients.evalJavaScript("zk.$('" + this.getUuid() + "').clearInit()");
    }

    public String showOption() {
        return JSON.toJSONString(this.option);
    }

    public String showSeries() {
        return JSON.toJSONString(this.option.getSeries());
    }

    /**
     * 设置高亮目标数据
     * @param dataIndex 数据id
     * @param always 是否一直亮着
     */
    public void setHighLight(int dataIndex, boolean always) {
        Clients.evalJavaScript("zk.$('" + this.getUuid() + "').setHighLight(" + dataIndex + "," + always + ")");
    }
}
